# -*- coding: utf-8 -*-
__author__ = 'lztkdr'
__date__ = '2020/4/18 22:13'

"""
	无监督学习：k-means聚类
	
"""

import pandas as pd
from sklearn.decomposition import PCA
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
from sklearn.metrics import silhouette_score

"""
	k-means步骤
		1、随机设置K个特征空间内的点作为初始的聚类中心
	
		2、对于其他每个点计算到K个中心的距离，未知的点选择最近的一个聚类
		中心点作为标记类别
		
		3、接着对着标记的聚类中心之后，重新计算出每个聚类的新中心点（平
		均值）
		
		4、如果计算得出的新中心点与原中心点一样，那么结束，否则重新进行
		第二步过程
	--------------------------------------------------------------	
	一个样本与其所在相同聚类的样本的平均距离，这个距离越小，说明这个样本越应该被分配到当前的聚类中。
	一个样本与其距离最近的下一个聚类里的点的平均距离，这个距离越大，表示这个样本越不应该被分配到其他聚类中。
	
	sklearn.cluster.KMeans(n_clusters=8,init=‘k-means++’)
		k-means聚类
		n_clusters:开始的聚类中心数量
		init:初始化方法，默认为'k-means ++’
		labels_:默认标记的类型，可以和真实值比较（不是值比较）
	
	聚类做在 分类之前。

"""

# Pycharm输出窗口有省略号，数据显示不全解决方法
pd.set_option('display.max_rows', 10)
pd.set_option('display.max_columns', 200)
pd.set_option('display.width', 1000)

print('pd.__version__:', pd.__version__)

print('============================================================================================================')


def kmeans():
	# order_products__prior.csv 订单与商品信息
	prior = pd.read_csv("./instacart/order_products__prior.csv")
	print('prior columns:\n', prior.columns)

	# product.csv 商品信息
	products = pd.read_csv("./instacart/products.csv")
	print('products columns:\n', products.columns)

	# order.csv 用户的订单信息
	orders = pd.read_csv("./instacart/orders.csv")
	print('orders columns:\n', orders.columns)

	# aisles.csv 商品所属具体物品类别
	aisles = pd.read_csv("./instacart/aisles.csv")
	print('aisles columns:\n', aisles.columns)

	# prior columns:
	#  Index(['order_id', 'product_id', 'add_to_cart_order', 'reordered'], dtype='object')
	# products columns:
	#  Index(['product_id', 'product_name', 'aisle_id', 'department_id'], dtype='object')
	# orders columns:
	#  Index(['order_id', 'user_id', 'eval_set', 'order_number', 'order_dow', 'order_hour_of_day', 'days_since_prior_order'], dtype='object')
	# aisles columns:
	#  Index(['aisle_id', 'aisle'], dtype='object')

	print("======================================================================")

	# 合并四张表到一张表  （用户-物品类别）
	_mg = pd.merge(prior, products, on=['product_id', 'product_id'])
	_mg = pd.merge(_mg, orders, on=['order_id', 'order_id'])
	mt = pd.merge(_mg, aisles, on=['aisle_id', 'aisle_id'])

	### 用户、商品类别 进行分组
	# 交叉表（特殊的分组工具）
	df = pd.crosstab(mt['user_id'], mt['aisle'])
	print(df.shape)  # (206209, 134)
	print(df.columns)
	print("====================================")

	# print(df.head())

	# 进行主成分分析
	pca = PCA(n_components=0.9)
	data = pca.fit_transform(df)

	# 主成分分析后，数据被降维(减少)
	print(data.shape)  # (206209, 27)
	print(type(data))  # numpy.ndarray

	# 把样本数量减少
	x = data[:500]
	print(type(x))  # numpy.ndarray
	print(x.shape)  # (500, 27)
	# 假设用户一共分为四个类别
	km = KMeans(n_clusters=4)
	km.fit(x)
	predict = km.predict(x)
	# 显示聚类的结果
	plt.figure(figsize=(10, 10))
	# 建立四个颜色的列表
	colored = ['orange', 'green', 'blue', 'purple']
	colr = [colored[i] for i in predict]
	# 以列索引 1 作为 x ，列索引20 作为y 传入
	plt.scatter(x[:, 1], x[:, 20], color=colr)
	plt.xlabel("1")
	plt.ylabel("20")
	plt.show()

	"""
	sklearn.metrics.silhouette_score(X, labels)
		计算所有样本的平均轮廓系数
		X：特征值
		labels：被聚类标记的目标值
	"""
	# 评判聚类效果，轮廓系数
	# 轮廓系数处于-1~1之间，值越大，表示聚类效果越好。
	# from sklearn.metrics import silhouette_score
	# x: 传入500个样本的特征值，predict:500个样本标记的类别
	# 一般来说，距离效果很难超过 0.7 左右，0.63 已经非常好了。
	print(silhouette_score(x, predict)) # 0.6330290225443548

	"""
	KMeans(n_clusters=4)
	n_clusters 指定多少呢？
	知道 类别个数时，直接用就好，不知道时，根据 silhouette_score 轮廓系数 得出的效果 得出最优值。
	"""
	return None


if __name__ == "__main__":
	"""
	K-means 总结：
	
		特点分析：
			采用迭代式算法，直观易懂并且非常实用
			
		缺点：容易收敛到局部最优解(多次聚类)
	         需要预先设定簇的数量(k-means++解决)
	"""
	kmeans()
